package com.lanchetech.user.service.impl;


import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.dto.ActivityDTO;
import com.lanchetech.bean.dto.AppointmentFeatureDTO;
import com.lanchetech.bean.dto.AppointmentHourDTO;
import com.lanchetech.bean.request.*;
import com.lanchetech.bean.response.*;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.*;
import com.lanchetech.common.constants.JypConstant;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.exception.ResultCodeException;
import com.lanchetech.common.util.ObjectUtil;
import com.lanchetech.common.util.SensitiveInfoUtil;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import com.lanchetech.service.FileService;
import com.lanchetech.service.InviteCodeService;
import com.lanchetech.service.PublicService;
import com.lanchetech.user.service.ProductService;
import com.lanchetech.user.service.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lanchetech.common.constants.JypConstant.MAX_READ_COUNT;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    SpuMapper spuMapper;

    @Autowired
    FlashSaleActivityMapper flashSaleActivityMapper;

    @Autowired
    FlashSaleSpuMapper flashSaleSpuMapper;

    @Autowired
    FlashSaleSkuMapper flashSaleSkuMapper;

    @Autowired
    SkuMapper skuMapper;

    @Autowired
    SpuAttributeMapper spuAttributeMapper;

    @Autowired
    FrontBackCategoryMapper frontBackCategoryMapper;

    @Autowired
    UserCollectionMapper userCollectionMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    CommentStatisticsMapper commentStatisticsMapper;

    @Autowired
    OrderCommentMapper orderCommentMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    UserService userService;

    @Autowired
    ShoppingCartMapper shoppingCartMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    PackagesMapper packagesMapper;

    @Autowired
    PackageSkuMapper packageSkuMapper;

    @Autowired
    CouponMapper couponMapper;

    @Autowired
    FileService fileService;

    @Autowired
    InviteCodeService inviteCodeService;

    @Value("${domain.url}")
    private String domainUrl;

    @Autowired
    FreightMapper freightMapper;

    @Autowired
    FreightListMapper freightListMapper;

    @Autowired
    WebConfigMapper webConfigMapper;

    @Autowired
    ArticleMapper articleMapper;

    @Autowired
    OrderSkuMapper orderSkuMapper;

    @Autowired
    OrderAddressMapper orderAddressMapper;

    @Autowired
    AppointmentSettingMapper appointmentSettingMapper;

    @Autowired
    BargainSkuMapper bargainSkuMapper;

    @Autowired
    ShopMapper shopMapper;

    @Autowired
    BargainSpuMapper bargainSpuMapper;

    @Autowired
    BargainActivityMapper bargainActivityMapper;

    @Autowired
    BargainRecordMapper bargainRecordMapper;

    @Autowired
    TenantMapper tenantMapper;

    @Autowired
    RecommendSpuMapper recommendSpuMapper;

    @Autowired
    MerchantMapper merchantMapper;

    @Autowired
    ZoneMapper zoneMapper;

    @Autowired
    ZoneSpuMapper zoneSpuMapper;

    @Autowired
    ShopStatisticsMapper shopStatisticsMapper;

    @Autowired
    SelectionApplyMapper selectionApplyMapper;

    @Autowired
    SelectionMapper selectionMapper;

    @Autowired
    UserTagMapper userTagMapper;

    @Autowired
    CategoryMapper categoryMapper;

    @Autowired
    SpuStatisticsMapper spuStatisticsMapper;

    @Autowired
    SubscribeRecordMapper subscribeRecordMapper;

    @Autowired
    AnchorWindowsMapper anchorWindowsMapper;

    @Autowired
    ShopAmountMapper shopAmountMapper;

    @Autowired
    IndustryMapper industryMapper;
    @Autowired
    private ShareActivitySpuMapper shareActivitySpuMapper;
    @Autowired
    private PublicService publicService;
    @Autowired
    private CardConsumptionFundMapper cardConsumptionFundMapper;
    @Autowired
    private CardConsumptionFundItemMapper cardConsumptionFundItemMapper;

    // 原版
    @Override
    public ResultData<FlashSaleVO> getFlashSaleActivity(PageReq req, User user) {
        FlashSaleVO flashSaleVO = new FlashSaleVO();
        FlashSaleActivity activity = new FlashSaleActivity();
        if (FlashSaleSceneEnum.PAGE.getScene().equals(req.getScene())) {
            activity = flashSaleActivityMapper.selectByPrimaryKey(req.getActivityId());
        } else {
            // 获取当前进行中，或者今日最近的限时优惠
            // 先获取当前进行中的活动
            activity = flashSaleActivityMapper.findOneStarting();
            if (activity == null) {
                // 获取今日最近的限时优惠
                activity = flashSaleActivityMapper.findOneLatest();
            }

            if (activity == null) {
                return new ResultData<>(null);
            } else {
                req.setActivityId(activity.getId());
            }

            if (FlashSaleSceneEnum.MENU.getScene().equals(req.getScene())) {
                // 获取今日的全部条数
                flashSaleVO.setMenus(flashSaleActivityMapper.findAllToday());
            }
        }

        flashSaleVO.setActivity(activity);
        req.setActivityId(activity.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<FlashSaleSpuItemVO> list = flashSaleSpuMapper.findAllPageForUser(req);
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, Spu> spuHashMap = daoService.getSpuMap(list.stream().map(FlashSaleSpuItemVO::getSpuId).collect(Collectors.toList()));
            List<FlashSaleSku> saleSkuList = flashSaleSkuMapper.findAllByActivityIdAndSpuIds(activity.getId(), list.stream().map(FlashSaleSpuItemVO::getSpuId).collect(Collectors.toList()));
            Map<Long, FlashSaleSku> skuMap = new HashMap<>();
            saleSkuList.stream().forEach(item -> skuMap.put(item.getSpuId(), item));
            Map<Long, Sku> specSkuMap = daoService.getSkuMap(saleSkuList.stream().map(FlashSaleSku::getSkuId).collect(Collectors.toList()));
            List<Long> subscribeSpuIds = new ArrayList<>();
            if (user != null) {
                subscribeSpuIds = subscribeRecordMapper.findRelateIdByUserIdAndTypeAndActivityId(user.getId(), SubscribeRecordTypeEnum.FLASH_SALE.getType(), activity.getId());
            }
            List<Long> finalSubscribeSpuIds = subscribeSpuIds;
            list.stream().forEach(item -> {
                item.setMainImage(spuHashMap.get(item.getSpuId()).getMainImage());
                item.setName(spuHashMap.get(item.getSpuId()).getName());
                item.setOriginalPrice(spuHashMap.get(item.getSpuId()).getOriginalPrice());
                item.setPrice(spuHashMap.get(item.getSpuId()).getMinPrice());
                item.setFlashSaleSku(skuMap.get(item.getSpuId()));
                item.setSpecNames(specSkuMap.get(item.getFlashSaleSku().getSkuId()).getSpecNames());
                item.setSpecValues(specSkuMap.get(item.getFlashSaleSku().getSkuId()).getSpecValues());
                item.setIsSubscribe(finalSubscribeSpuIds == null ? false : finalSubscribeSpuIds.contains(item.getId()));
            });
        }
        flashSaleVO.setSpuList(PageHelpUtil.buildPage(list, page).getValue());
        return new ResultData<>(flashSaleVO);
    }

    @Override
    public ResultData<BasePageResp<Packages>> getPackagePage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Packages> list = packagesMapper.findAllFrontPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<PackageResp> getPackageDetail(Long id) {
        return new ResultData<>(daoService.getPackageDetail(id));
    }

    private ProductResp commonProductDetail(ProductResp resp, ProductReq req, User user) {
        // 以下是商品详情不缓存的部分
        if (req.getActivityType() != null) {
            if (ActivityEnum.FLASH_SALE.getActivity().equals(req.getActivityType())) {
                FlashSaleSpu flashSaleSpu = flashSaleSpuMapper.findOneByActivityIdAndSpuId(req.getActivityId(), req.getSpuId());
                FlashSaleActivity flashSaleActivity = flashSaleActivityMapper.selectByPrimaryKey(req.getActivityId());
                ActivityDTO activityDTO = ActivityDTO.builder().activityId(req.getActivityId()).activityType(req.getActivityType()).startTime(flashSaleActivity.getStartTime()).endTime(flashSaleActivity.getEndTime()).limitCount(flashSaleSpu.getLimitCount()).price(flashSaleSpu.getMinPrice()).tagImage(flashSaleSpu.getTagImage()).build();

                if (user != null) {
                    activityDTO.setIsSubscribe(subscribeRecordMapper.findOneByUserIdAndRelateIdAndType(user.getId(), flashSaleSpu.getId(), SubscribeRecordTypeEnum.FLASH_SALE.getType()) != null);
                }
                resp.setActivity(activityDTO);
                Long activityCount = flashSaleSkuMapper.sumSpuStock(req.getSpuId(), req.getActivityId());
                if (activityCount == null) {
                    activityCount = 0L;
                }
                resp.setActivityCount(activityCount);
            }
            if (ActivityEnum.BARGAIN.getActivity().equals(req.getActivityType())) {
                BargainSpu bargainSpu = bargainSpuMapper.findOneByActivityIdAndSpuId(req.getActivityId(), req.getSpuId());
                BargainActivity bargainActivity = bargainActivityMapper.selectByPrimaryKey(req.getActivityId());
                ActivityDTO activityDTO = ActivityDTO.builder().activityId(req.getActivityId()).activityType(req.getActivityType()).startTime(bargainActivity.getStartTime()).endTime(bargainActivity.getEndTime()).limitCount(bargainSpu.getLimitCount()).price(bargainSpu.getMinPrice()).stock(bargainActivity.getHelperCount()).build();
                resp.setActivity(activityDTO);
            }
        }

        // 不缓存，获取最近购买者10条，并获取头像，昵称，地址
        List<OrderSku> orderSkuList = orderSkuMapper.findAllBySpuIdAndStatusLimit10(req.getSpuId());
        if (!CollectionUtils.isEmpty(orderSkuList)) {
            Map<Long, User> userMap = daoService.getUserMap(orderSkuList.stream().map(OrderSku::getUserId).collect(Collectors.toList()));
            List<OrderAddress> orderAddressList = orderAddressMapper.findAllByOrderIds(orderSkuList.stream().map(OrderSku::getOrderId).collect(Collectors.toList()));
            Map<Long, OrderAddress> orderAddressMap = new HashMap<>();
            orderAddressList.stream().forEach(item -> orderAddressMap.put(item.getOrderId(), item));

            List<RecentBuyVO> recentBuyVOList = new ArrayList<>();
            orderSkuList.stream().forEach(item -> {
                recentBuyVOList.add(RecentBuyVO.builder().mergerName(orderAddressMap.containsKey(item.getOrderId()) ? orderAddressMap.get(item.getOrderId()).getMergerName() : ",,,").avatar(userMap.get(item.getUserId()).getAvatar()).nickname(userMap.get(item.getUserId()).getNickname()).createdAt(item.getCreatedAt()).build());
            });
            resp.setRecentBuyVOList(recentBuyVOList);
        }

        // 获取商品的优惠券(不缓存优惠券)，商品详情只能领取使用商家自己的优惠券
        PageReq pageReq = new PageReq();
        pageReq.setStatus(ToggleEnum.ON.getStatus());
        pageReq.setNow(new Date());
        pageReq.setRelateId(req.getSpuId());
        pageReq.setTenantId(resp.getSpu().getTenantId());
        resp.setCouponList(couponMapper.findTwo(pageReq));

        return resp;
    }

    @Override
    public ResultData<ProductResp> getProductDetail(ProductReq req, User user) {
        Spu spu = spuMapper.selectByPrimaryKey(req.getSpuId());

        if (spu == null) {
            ResultData<ProductResp> resultData = new ResultData<>();
            resultData.setResultCode(ResultCode.SPU_EXCEPTION);
            return resultData;
        }

        //更新spu统计数据
        updateSpuStatistics(spu.getId(), user, spu.getTenantId());

        //更新商店统计数据 商店浏览量+1
        if (spu != null && spu.getShopId() != null) {
            shopStatisticsMapper.updateViewCountByShopId(spu.getShopId());
        }

        ValueOperations<String, ProductResp> operations = redisTemplate.opsForValue();
        String key = "ProductDetail:" + req.getSpuId() + ":" + DigestUtils.md5DigestAsHex((JSON.toJSONString(req)).getBytes());
        ProductResp resp = operations.get(key);
        if (resp != null) {
            // 获取店铺推荐商品
            List<RecommendSpu> recommendSpuList = recommendSpuMapper.findAllByTenantId(resp.getSpu().getTenantId());
            if (!CollectionUtils.isEmpty(recommendSpuList)) {
                List<Spu> recommendSpu = spuMapper.findAllByIds(recommendSpuList.stream().map(RecommendSpu::getSpuId).collect(Collectors.toList()));
                resp.setRecommendSpuList(recommendSpu);
            }

            //是否存在用户标签
            if (user != null) {
                noExistUserTag(req.getSpuId());

                Long count = orderSkuMapper.sumBuyCount(req.getSpuId(), user.getId());
                resp.setBuyCount(count != null ? count : 0);
            }

            return new ResultData<>(commonProductDetail(resp, req, user));
        }

        resp = new ProductResp();
        resp.setSpu(spu);
        List<OrderCommentResp> commentList = orderCommentMapper.findLatestTwo(spu.getId());
        if (CollectionUtils.isEmpty(commentList)) {
            commentList = new ArrayList<>();
        } else {
            List<Long> userIds = new ArrayList<>();
            commentList.stream().forEach((item) -> {
                if (!userIds.contains(item.getUserId())) {
                    userIds.add(item.getUserId());
                }
            });
            List<User> userList = userMapper.findAllByUserIds(userIds);
            Map<Long, User> userMap = new HashMap<>();
            Map<Long, Sku> skuMap = daoService.getSkuMap(commentList.stream().map(OrderCommentResp::getSkuId).collect(Collectors.toList()));
            userList.stream().forEach(item -> userMap.put(item.getId(), item));
            commentList.stream().forEach((item) -> {
                item.setAvatar(item.getAnonymous().equals((byte) 0) ? userMap.get(item.getUserId()).getAvatar() : JypConstant.ANONYMOUS_AVATAR);
                item.setNickname(item.getAnonymous().equals((byte) 0) ? userMap.get(item.getUserId()).getNickname() : SensitiveInfoUtil.chineseName(userMap.get(item.getUserId()).getNickname()));
                item.setSpecNames(skuMap.get(item.getSkuId()).getSpecNames());
                item.setSpecValues(skuMap.get(item.getSkuId()).getSpecValues());
            });
        }

        CommentStatistics commentStatistics = commentStatisticsMapper.findOneBySpuId(spu.getId());
        if (commentStatistics == null) {
            commentStatistics = new CommentStatistics();
        }
        resp.setOrderCommentList(commentList);
        resp.setCommentStatistics(commentStatistics);

        // 获取和该商品相关的套餐
        List<PackageSku> packageSkuList = packageSkuMapper.findAllBySpuId(spu.getId());
        if (!CollectionUtils.isEmpty(packageSkuList)) {
            List<Long> packageActivityIds = new ArrayList<>();
            packageSkuList.stream().forEach(item -> {
                if (!packageActivityIds.contains(item.getActivityId())) {
                    packageActivityIds.add(item.getActivityId());
                }
            });

            List<Packages> packages = packagesMapper.findTwoTopInIds(new Date(), packageActivityIds);
            if (!CollectionUtils.isEmpty(packages)) {
                resp.setPackageList(packages);
            }
        }

        String articleId = webConfigMapper.findValueByName(WebConfigEnum.AFTER_SALE_ARTICLE_ID.getConfig());
        if (!StringUtils.isEmpty(articleId)) {
            resp.setAfterSaleContent(articleMapper.selectByPrimaryKey(new Long(articleId)).getContent());
        }

        // 获取猜你喜欢，按销量高低排序的前30条商品
        resp.setLikeSpuList(daoService.findAllLikeSpu(spu.getCategoryId()));

        // 运费
        resp.setFreight(freightMapper.selectByPrimaryKey(spu.getFreightId()));

        // 获取店铺推荐商品
        List<RecommendSpu> recommendSpuList = recommendSpuMapper.findAllByTenantId(spu.getTenantId());
        if (!CollectionUtils.isEmpty(recommendSpuList)) {
            List<Spu> recommendSpu = spuMapper.findAllByIds(recommendSpuList.stream().map(RecommendSpu::getSpuId).collect(Collectors.toList()));
            resp.setRecommendSpuList(recommendSpu);
        }

        TenantInfoVO vo = shopMapper.getTenantInfoById(spu.getTenantId());
        Tenant tenant = tenantMapper.selectByPrimaryKey(spu.getTenantId());
        Shop shop = shopMapper.selectByPrimaryKey(spu.getTenantId());
        if (tenant != null) {
            vo.setMerchantId(tenant.getMerchantId());
            vo.setImageUrl(tenant.getImageUrl());
            vo.setDescription(tenant.getDescription());
        }
        vo.setTenantName(shop.getShopName());
        vo.setTenantNickname(shop.getShopName());

        vo.setStar(shop.getOverallStar().doubleValue());
        vo.setServiceStar(shop.getServiceStar().doubleValue());
        vo.setEnvironmentStar(shop.getEnvironmentStar().doubleValue());
        vo.setValueStar(shop.getValueStar().doubleValue());
        vo.setSpuNumber(spuMapper.countByTenantId(shop.getId()));
        vo.setShopLevel(shop.getShopLevel());
        resp.setTenantInfoVO(vo);

        if (SpuTypeEnum.PARTNER.getType().equals(spu.getType())) {
            resp.setPartnerPrice(skuMapper.findMinPartnerPriceBySpuId(req.getSpuId()));
        }

        // 是否为分享赚商品、活动是否开启
//        if (ActivityEnum.SHARE.getActivity().equals(req.getActivityType())) {
        share(spu, resp);
//        }

        // 如果是商家商品
        if (SpuTypeEnum.SHOP.getType().equals(spu.getType())) {
            AppointmentSetting appointmentSetting = appointmentSettingMapper.findOneBySpuId(spu.getId());
            if (appointmentSetting != null) {
                resp.setAppointmentSetting(appointmentSetting);
            }
        }

        // 是否为消费金商品
        if (ActivityEnum.CONSUMPTION_FUND.getActivity().equals(req.getActivityType())) {
            cardConsumptionFund(req, user, spu, resp);
        }
//        operations.set(key, resp, 10, TimeUnit.MINUTES);

        //是否存在用户标签
        if (user != null) {
            noExistUserTag(req.getSpuId());

            Long count = orderSkuMapper.sumBuyCount(req.getSpuId(), user.getId());
            resp.setBuyCount(count != null ? count : 0);
        }

        SpuPartnerPriceVO spuPartnerPriceVO = skuMapper.findMinPartnerPriceVOBySpuId(spu.getId());
        if (spuPartnerPriceVO != null) {
            resp.setPartnerPrice(spuPartnerPriceVO.getPartnerPrice());
            resp.setVipPrice(spuPartnerPriceVO.getVipPrice());
        }
        operations.set(key, resp, 10, TimeUnit.MINUTES);
        return new ResultData<>(commonProductDetail(resp, req, user));
    }

    /**
     * 消费金商品
     *
     * @param req
     * @param user
     * @param spu
     * @param resp
     */
    private void cardConsumptionFund(ProductReq req, User user, Spu spu, ProductResp resp) {
        CardConsumptionFund cardConsumptionFund = cardConsumptionFundMapper.selectByPrimaryKey(req.getActivityId());
        if (null != cardConsumptionFund) {
            // 判断用户是否为首次折扣
            int count = orderSkuMapper.getOrderSkuByUserId(user.getId(), ActivityEnum.CONSUMPTION_FUND.getActivity());
            CardConsumptionFundItem oneFundItemBySupId = cardConsumptionFundItemMapper.getOneFundItemBySupId(cardConsumptionFund.getId(), spu.getId());
            if (count == 0) {
                BigDecimal firstDiscount = spu.getMaxPrice().multiply(oneFundItemBySupId.getFirstDiscount());
                resp.setLowDiscount(firstDiscount);
            } else {
                BigDecimal followDiscount = spu.getMaxPrice().multiply(oneFundItemBySupId.getFollowDiscount());
                resp.setHighDiscount(followDiscount);
            }
        }
    }

    /**
     * 分销商品
     *
     * @param resp
     */
    private void share(Spu spu, ProductResp resp) {
        List<ShareActivitySpuVo> shareActivitySpu = shareActivitySpuMapper.findAllBySpuId(spu.getId());
        if (CollectionUtils.isNotEmpty(shareActivitySpu)) {
            BigDecimal maxCommission = BigDecimal.ZERO;
            BigDecimal maxHiCoin = BigDecimal.ZERO;
            for (ShareActivitySpuVo shareActivitySpuVo : shareActivitySpu) {
                if (shareActivitySpuVo.getType()) {
                    // 固定金额  最高可得佣金
                    BigDecimal regularCommission = shareActivitySpuVo.getCommissionScale();
                    // 固定金额  最高可得嗨币
                    BigDecimal regularHiCoin = shareActivitySpuVo.getHiCoinScale();
                    if (regularCommission.compareTo(maxCommission) > 0) maxCommission = regularCommission;
                    if (regularHiCoin.compareTo(maxHiCoin) > 0) maxHiCoin = regularHiCoin;
                } else {
                    ShareSkuVo shareSkuVo = skuMapper.getSkuById(shareActivitySpuVo.getSkuId());
                    // 百分比    最高可得佣金
                    BigDecimal percentageCommission = shareActivitySpuVo.getCommissionScale().multiply(shareSkuVo.getPrice()).setScale(2, RoundingMode.HALF_DOWN);
                    // 百分比    最高可得嗨币
                    BigDecimal percentageHiCoin = shareActivitySpuVo.getHiCoinScale().multiply(shareSkuVo.getPrice()).setScale(2, RoundingMode.HALF_DOWN);
                    if (percentageCommission.compareTo(maxCommission) > 0) maxCommission = percentageCommission;
                    if (percentageHiCoin.compareTo(maxHiCoin) > 0) maxHiCoin = percentageHiCoin;
                }
            }
            resp.setShare(true);
            resp.setShareMaxPrice(maxCommission);
            resp.setShareHiCoinMaxPrice(maxHiCoin);
            resp.setShareActivityId(shareActivitySpu.get(0).getShareActivityId());
        }
    }

    private void noExistUserTag(Long spuId) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        List<UserTag> userTags = userTagMapper.findAllByUserIdAndType(user.getId(), UserTagTypeEnum.SPU.getType());
        if (CollectionUtils.isEmpty(userTags)) {
            ValueOperations<String, Integer> operations = redisTemplate.opsForValue();
            Integer readCount = operations.get(UserTagTypeEnum.SPU.getType().toString() + spu.getCategoryId().toString());
            if (readCount == null) {
                operations.set(UserTagTypeEnum.SPU.getType().toString() + spu.getCategoryId().toString(), 1, 24, TimeUnit.HOURS);
            } else {
                readCount++;
                if (readCount >= MAX_READ_COUNT) {
                    Category category = categoryMapper.selectByPrimaryKey(spu.getCategoryId());
                    userTagMapper.insert(UserTag.builder().userId(user.getId()).type(UserTagTypeEnum.SPU.getType()).weight(1).relateId(spu.getCategoryId()).tagName(category.getTitle()).build());
                } else {
                    operations.set(UserTagTypeEnum.SPU.getType().toString() + spu.getCategoryId().toString(), readCount, 24, TimeUnit.HOURS);
                }
            }
        }
    }

    private void updateSpuStatistics(Long spuId, User user, Long tenantId) {
        ValueOperations<String, Integer> operations = redisTemplate.opsForValue();
        Boolean isHourFirst = false;
        if (user != null) {
            String key = "SpuStatistics:" + spuId.toString() + ":" + user.getId().toString();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            Date hourStart = getHourStartTime(calendar).getTime();
            SpuStatistics spuStatistics = spuStatisticsMapper.findOneBySpuIdAndDateTime(spuId, hourStart);
            if (spuStatistics == null) {
                spuStatisticsMapper.insert(SpuStatistics.builder().spuId(spuId).dateTime(hourStart).uv(1L).pv(0L).tenantId(tenantId).build());
                operations.set(key, 1, getHourSurplusSeconds(), TimeUnit.SECONDS);
            } else {
                if (operations.get(key) == null) {
                    isHourFirst = true;
                    operations.set(key, 1, getHourSurplusSeconds(), TimeUnit.SECONDS);
                }
            }
        }
        spuStatisticsMapper.addUvAndPv(spuId, isHourFirst ? 1L : 0L);
    }


    /**
     * 小时剩余秒数
     *
     * @return
     */
    private Long getHourSurplusSeconds() {
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR_OF_DAY) + 1; // 24小时制
        LocalDateTime midnight;
        if (hour > 23) {
            midnight = LocalDateTime.now().plusDays(1).withHour(hour - 24).withMinute(0).withSecond(0).withNano(0);
        } else {
            midnight = LocalDateTime.now().plusDays(0).withHour(hour).withMinute(0).withSecond(0).withNano(0);
        }
        long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), midnight);
        return seconds;
    }

    private Calendar getHourStartTime(Calendar calendar) {
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        return calendar;
    }

    @Override
    public ResultData<List<AppointmentHourDTO>> getSpuAppointmentByDate(PageReq req) {
        return new ResultData<>(orderSkuMapper.countAllByAppointmentTime(req));
    }

    @Override
    public ResultData<Spu> getSpu(Long id) {
        return new ResultData<>(spuMapper.selectByPrimaryKey(id));
    }

    @Override
    public ResultData<List<Sku>> getSkuList(ProductReq req, User user) throws ResultCodeException {
        ValueOperations<String, List<Sku>> operations = redisTemplate.opsForValue();
        String key = "SkuList:" + req.getSpuId() + ":" + DigestUtils.md5DigestAsHex((JSON.toJSONString(req)).getBytes());
        List<Sku> resp = operations.get(key);
        if (CollectionUtils.isNotEmpty(resp)) {
            return new ResultData<>(resp);
        }

        if (req.getActivityType() == null
                || ActivityEnum.SHARE.getActivity().equals(req.getActivityType())
                || ActivityEnum.CONSUMPTION_FUND.getActivity().equals(req.getActivityType())) {
            resp = skuMapper.findAllBySpuIdForUser(req.getSpuId());
        }
        if (ActivityEnum.SHARE.getActivity().equals(req.getActivityType())) {
            List<Long> skuIds = resp.stream().map(Sku::getId).collect(Collectors.toList());
            List<ShareActivitySpuVo> activitySpus = shareActivitySpuMapper.getShareActivitySkuByIds(skuIds);
            if (CollectionUtils.isNotEmpty(activitySpus)) {
                for (ShareActivitySpuVo activitySpuVo : activitySpus) {
                    for (Sku sku : resp) {
                        if (activitySpuVo.getSkuId().equals(sku.getId())) {
                            // 固定金额
                            if (activitySpuVo.getType()) {
                                sku.setCommissionScale(activitySpuVo.getCommissionScale());
                                sku.setHiCoinScale(activitySpuVo.getHiCoinScale());
                            } else {
                                // 百分比
                                sku.setCommissionScale(sku.getPrice().multiply(activitySpuVo.getCommissionScale()).setScale(2, RoundingMode.HALF_DOWN));
                                sku.setHiCoinScale(sku.getPrice().multiply(activitySpuVo.getHiCoinScale()).setScale(2, RoundingMode.HALF_DOWN));
                            }
                            // 分享赚佣金+嗨币 大于商品的售价 则超出分享赚商品价格异常
                            if (sku.getCommissionScale().add(sku.getHiCoinScale()).compareTo(sku.getPrice()) > 0) {
                                throw new ResultCodeException(ResultCode.SHARE_PRICE_UNUSUAL);
                            }
                            // 放入活动序号
                            sku.setShareActivityId(activitySpuVo.getShareActivityId());
                        }
                    }
                }
            }
        }
        // 消费金商品
        if (ActivityEnum.CONSUMPTION_FUND.getActivity().equals(req.getActivityType())) {
            CardConsumptionFund cardConsumptionFund = cardConsumptionFundMapper.selectByPrimaryKey(req.getActivityId());
            if (null != cardConsumptionFund) {
                resp.forEach(sku -> {
                    // 判断用户是否为首次折扣
                    int count = orderSkuMapper.getOrderSkuByUserId(user.getId(), ActivityEnum.CONSUMPTION_FUND.getActivity());
                    CardConsumptionFundItem oneFundItemBySupId = cardConsumptionFundItemMapper.getOneFundItemBySupId(cardConsumptionFund.getId(),
                            sku.getSpuId());
                    if (count == 0) {
                        // 首次抵扣金额
                        BigDecimal firstDiscount = sku.getPrice().multiply(oneFundItemBySupId.getFirstDiscount());
                        sku.setLowDiscount(firstDiscount);
                    } else {
                        // 后续抵扣金额
                        BigDecimal followDiscount = sku.getPrice().multiply(oneFundItemBySupId.getFollowDiscount());
                        sku.setHighDiscount(followDiscount);
                    }
                });
            }
        }


        if (ActivityEnum.FLASH_SALE.getActivity().equals(req.getActivityType())) {
            List<FlashSaleSku> list = flashSaleSkuMapper.findAllByActivityIdAndSpuId(req.getActivityId(), req.getSpuId());
            Map<Long, Sku> skuMap = daoService.getSkuMap(list.stream().map(FlashSaleSku::getSkuId).collect(Collectors.toList()));
            List<Sku> result = new ArrayList<>();
            list.stream().forEach(item -> {
                skuMap.get(item.getSkuId()).setPrice(item.getPrice());
                skuMap.get(item.getSkuId()).setStock(item.getStock());
                result.add(skuMap.get(item.getSkuId()));
            });
            resp = result;
        }

        if (ActivityEnum.BARGAIN.getActivity().equals(req.getActivityType())) {
            List<BargainSku> list = bargainSkuMapper.findAllByActivityIdAndSpuId(req.getActivityId(), req.getSpuId());
            Map<Long, Sku> skuMap = daoService.getSkuMap(list.stream().map(BargainSku::getSkuId).collect(Collectors.toList()));
            List<Sku> result = new ArrayList<>();
            list.stream().forEach(item -> {
                skuMap.get(item.getSkuId()).setPrice(item.getPrice());
                skuMap.get(item.getSkuId()).setStock(item.getStock());
                result.add(skuMap.get(item.getSkuId()));
            });
            resp = result;
        }
        operations.set(key, resp, 10, TimeUnit.MINUTES);
        return new ResultData<>(resp);
    }

    @Override
    public ResultData<List<SpuAttributeVO>> getAttributeList(Long spuId) {
        return new ResultData<>(spuAttributeMapper.findAllBySpuIdWithName(spuId));
    }

    public boolean computedIsUseCache(SpuPageReq req) {
        // 只缓存首页,只缓存固定的几个前台类目商品
        return req.getPageNo() == 1 && FrontCategoryCacheEnum.isInFrontCategoryCache(req.getFrontCategoryId()) && req.getName() == null && req.getMinPrice() == null;
    }

    @Override
    public ResultData<BasePageResp<SpuResp>> getSpuPage(SpuPageReq req, User user) {
        boolean isUseCache = computedIsUseCache(req);
        ValueOperations<String, ResultData<BasePageResp<SpuResp>>> operations = redisTemplate.opsForValue();
        String key = "SpuPage:" + DigestUtils.md5DigestAsHex((JSON.toJSONString(req)).getBytes());
        if (isUseCache) {
            ResultData<BasePageResp<SpuResp>> result = operations.get(key);
            if (result != null) {
                return result;
            }
        }

        if (req.getFrontCategoryId() != null) {
            List<FrontBackCategory> frontBackCategoryList = frontBackCategoryMapper.findAllByFrontId(req.getFrontCategoryId());
            if (!CollectionUtils.isEmpty(frontBackCategoryList)) {
                List<Long> ids = frontBackCategoryList.stream().map(item -> item.getBackId()).collect(Collectors.toList());
                req.setBackCategoryIdList(ids);
            } else {
                req.setBackCategoryIdList(Arrays.asList(0L));
            }
        }

//        if (SpuTypeEnum.ORDINARY.getType().equals(req.getType())) {
//            req.setType(null);
//            req.setTypes(Arrays.asList(
//                    SpuTypeEnum.ORDINARY.getType(),
//                    SpuTypeEnum.NORMAL.getType(),
//                    SpuTypeEnum.COMMISSION.getType()
//            ));
//        }

        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        req.setFlashHidden(ToggleEnum.ON.getStatus());
        List<SpuResp> list = spuMapper.findAllPublicPage(req);
        if (CollectionUtils.isEmpty(list)) {
            return PageHelpUtil.buildPage(list, page);
        }

        List<Long> spuIds = list.stream().map(Spu::getId).collect(Collectors.toList());

        // 获取每个spu的评论数和好评数
        List<CommentStatistics> commentStatisticsList = commentStatisticsMapper.findAllBySpuIds(spuIds);
        Map<Long, CommentStatistics> commentMap = new HashMap<>();
        Map<Long, Shop> shopMap = daoService.getShopMap(list.stream().map(SpuResp::getShopId).collect(Collectors.toList()));

        if (!CollectionUtils.isEmpty(commentStatisticsList)) {
            commentStatisticsList.stream().forEach(item -> commentMap.put(item.getSpuId(), item));
        }

        Map<Long, Long> totalStatisticsVOMap = new HashMap<>();
        Boolean wantPv = req.getWantPv() != null && ToggleEnum.ON.getStatus().equals(req.getWantPv());
        if (wantPv) {
            List<TotalStatisticsVO> totalPvList = spuStatisticsMapper.getTotalPv(list.stream().map(SpuResp::getId).collect(Collectors.toList()));
            if (!CollectionUtils.isEmpty(totalPvList)) {
                totalPvList.stream().forEach(item -> {
                    totalStatisticsVOMap.put(item.getId(), item.getNumber());
                });
            }
        }

        //获取抢购人员
        Map<Long, List<OrderSku>> pendingOrderSkuMap = new HashMap<>();
        Map<Long, User> userMap = new HashMap<>();
        if (req.getType() != null && SpuTypeEnum.COMMISSION.getType().equals(req.getType())) {
            List<OrderSku> pendingOrderSkuList = orderSkuMapper.findAllBySpuIdInAndRushBuyStatus(list.stream().map(SpuResp::getId).collect(Collectors.toList()), RushBuyStatusEnum.PENDING.getStatus());
            if (!pendingOrderSkuList.isEmpty()) {
                userMap = daoService.getUserMap(pendingOrderSkuList.stream().map(OrderSku::getUserId).collect(Collectors.toList()));
                pendingOrderSkuList.stream().forEach(item -> {
                    List<OrderSku> orderSkuList = pendingOrderSkuMap.get(item.getSpuId());
                    if (orderSkuList == null) {
                        orderSkuList = new ArrayList<>();
                    }
                    if (orderSkuList.size() < 5) {
                        orderSkuList.add(item);
                        pendingOrderSkuMap.put(item.getSpuId(), orderSkuList);
                    }
                });
            }
        }


        //获取sku中小店最低价
        Map<Long, SpuPartnerPriceVO> spuPartnerPriceMap = new HashMap<>();
        Map<Long, AnchorWindows> anchorWindowsMap = new HashMap<>();
//        if (req.getType() != null && SpuTypeEnum.PARTNER.getType().equals(req.getType())) {
        List<SpuPartnerPriceVO> minPartnerPriceList = skuMapper.findMinPartnerPrice(list.stream().map(SpuResp::getId).collect(Collectors.toList()));
        if (!CollectionUtils.isEmpty(minPartnerPriceList)) {
            minPartnerPriceList.stream().forEach(item -> {
                spuPartnerPriceMap.put(item.getSpuId(), item);
            });
        }

        if (user != null) {
            List<AnchorWindows> anchorWindowsList = anchorWindowsMapper.findAllBySpuIdInAndAnchorId(list.stream().map(SpuResp::getId).collect(Collectors.toList()), user.getId());
            if (!CollectionUtils.isEmpty(anchorWindowsList)) {
                anchorWindowsList.stream().forEach(item -> {
                    anchorWindowsMap.put(item.getSpuId(), item);
                });
            }
        }
//        }


        Map<Long, User> finalUserMap = userMap;
        Map<Long, String> storeRankTagMap = new HashMap<>();
        list.forEach((item) -> {
            if (!storeRankTagMap.containsKey(item.getTenantId())) {
                String storeRankTag = storeRankTag(item.getTenantId());
                if (storeRankTag != null) {
                    item.setStoreRankTag(storeRankTag);
                    storeRankTagMap.put(item.getTenantId(), storeRankTag);
                }
            } else {
                item.setStoreRankTag(storeRankTagMap.get(item.getTenantId()));
            }

            if (commentMap.get(item.getId()) != null) {
                item.setCommentGood(commentMap.get(item.getId()).getFive() + commentMap.get(item.getId()).getFour());
                item.setCommentTotal(commentMap.get(item.getId()).getTotal());
            }
            if (SpuTypeEnum.EXCHANGE.getType().equals(req.getType())) {
                item.setShopStar(item.getShopId() != null ? shopMap.get(item.getShopId()).getOverallStar() : BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_DOWN));
            }
            Long number = totalStatisticsVOMap.get(item.getId());
            if (wantPv) {
                item.setPv(number == null ? 0 : number);
            }
            if (req.getType() != null && SpuTypeEnum.COMMISSION.getType().equals(req.getType())) {
                List<OrderSku> orderSkuList = pendingOrderSkuMap.get(item.getId());
                if (!CollectionUtils.isEmpty(orderSkuList)) {
                    List<RushOrderVO> rushOrderVOList = new ArrayList<>();
                    orderSkuList.stream().forEach(orderSku -> {
                        User itemUser = finalUserMap.get(orderSku.getUserId());
                        if (item != null) {
                            rushOrderVOList.add(RushOrderVO.builder().userNickname(itemUser.getNickname()).userAvatar(itemUser.getAvatar()).rushBuyStatus(orderSku.getRushBuyStatus()).userId(orderSku.getUserId()).build());
                        }
                    });
                    item.setRushOrderVOList(rushOrderVOList);
                }
            }

            //筛选合伙人商品
//            if (req.getType() != null && SpuTypeEnum.PARTNER.getType().equals(req.getType())) {
            if (spuPartnerPriceMap.containsKey(item.getId())) {
                item.setPartnerPrice(spuPartnerPriceMap.get(item.getId()).getPartnerPrice());
                item.setVipPrice(spuPartnerPriceMap.get(item.getId()).getVipPrice());
            }
            item.setIsAnchorWindows(anchorWindowsMap.containsKey(item.getId()));
//            }

            // 是否为分享赚商品
            List<ShareActivitySpuVo> shareActivitySpuVos = shareActivitySpuMapper.findAllBySpuId(item.getId());
            if (CollectionUtils.isNotEmpty(shareActivitySpuVos)) {
                PublicResp publicResp = publicService.setShareInformation(shareActivitySpuVos);
                item.setShareMaxPrice(publicResp.getShareMaxCommPrice());
                item.setShareHiCoinMaxPrice(publicResp.getShareHiCoinMaxPrice());
                item.setShare(publicResp.isShare());
                item.setShareActivityId(publicResp.getShareActivityId());
            }
        });

        ResultData<BasePageResp<SpuResp>> result = PageHelpUtil.buildPage(list, page);
        if (isUseCache) {
            operations.set(key, result, 10, TimeUnit.MINUTES);
        }

        //计算ftidf
        if (req.getName() != null && user != null) {
            tfidfMethod(req.getName(), UserTagTypeEnum.SPU.getType());
        }

        return result;
    }

    private String storeRankTag(Long tenantId) {
        ShopAmount shopAmount = shopAmountMapper.findOneByShopId(tenantId);
        if (shopAmount != null) {
            Long sameTotalRank = shopAmountMapper.countByAmountGreaterThanAndIndustryFirstId(shopAmount.getAmount(), shopAmount.getIndustryFirstId()) + 1;
            if (sameTotalRank < 4) {
                Industry industry = industryMapper.selectByPrimaryKey(shopAmount.getIndustryFirstId());
                return industry.getName() + "排行榜：TOP" + sameTotalRank;
            }
        }
        return null;
    }

    private void tfidfMethod(String search, Byte type) {
        ValueOperations<String, List<TagSpuStatisticsVO>> operations = redisTemplate.opsForValue();
        if (operations.get("tfidf" + type) != null) {
            tfidfUtil(operations.get("tfidf" + type), search, type);
        } else {
            //标题总字数
            List<StatisticsCountVO> totalWordCounts = spuMapper.getTotalWordCount();
            Map<Long, Long> totalWordCountMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(totalWordCounts)) {
                totalWordCounts.stream().forEach(item -> {
                    totalWordCountMap.put(item.getId(), item.getCount());
                });
            }

            //博文总数
            List<StatisticsCountVO> itemCount = spuMapper.getItemCount();
            Map<Long, Long> itemCountMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(itemCount)) {
                itemCount.stream().forEach(item -> {
                    itemCountMap.put(item.getId(), item.getCount());
                });
            }


            //计算统计数量
            List<TagSpuStatisticsVO> voList = new ArrayList<>();
            totalWordCounts.stream().forEach(item -> {
                TagSpuStatisticsVO vo = new TagSpuStatisticsVO();
                vo.setType(type);
                vo.setRelateId(item.getId());
                vo.setItemCount(itemCountMap.get(item.getId()));
                vo.setUpdateAt(new Date());
                vo.setTotalWordCount(totalWordCountMap.get(item.getId()));
                voList.add(vo);
            });
            operations.set("tfidf" + type, voList);

            tfidfUtil(voList, search, type);
        }
    }

    private void tfidfUtil(List<TagSpuStatisticsVO> list, String search, Byte type) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        ValueOperations<String, List<SearchTfidfVO>> operations = redisTemplate.opsForValue();
        List<SearchTfidfVO> searchTfidfVOS = new ArrayList<>();
        List<SearchTfidfVO> newSearchTfidfVO = new ArrayList<>();
        if (operations.get("tfidf" + search + type) != null) {
            searchTfidfVOS.addAll(operations.get("tfidf" + search + type));
        }

        Boolean isUseful = false;
        if (!CollectionUtils.isEmpty(searchTfidfVOS)) {
            isUseful = true;
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).getUpdateAt().after(searchTfidfVOS.get(0).getSearchTime())) {
                    isUseful = false;
                }
            }
        }
        if (searchTfidfVOS != null && isUseful) {
            //是否被其他用户查询过 且商品或博文没有变更
            Collections.sort(searchTfidfVOS, (o1, o2) -> o2.getTfidf().compareTo(o1.getTfidf()));
            newSearchTfidfVO.addAll(searchTfidfVOS);
        } else {
            Map<Long, Category> categoryMap = daoService.getCategoryMap(list.stream().map(TagSpuStatisticsVO::getRelateId).collect(Collectors.toList()));
            if (!org.springframework.util.CollectionUtils.isEmpty(list)) {

                //出现博文数量
                List<StatisticsCountVO> itemHaveCounts = spuMapper.countLikeSearch(search);
                Map<Long, Long> itemHaveCountMap = new HashMap<>();
                if (!org.springframework.util.CollectionUtils.isEmpty(itemHaveCounts)) {
                    itemHaveCounts.stream().forEach(item -> {
                        itemHaveCountMap.put(item.getId(), item.getCount());
                    });
                }

                //查询关键词出现次数
                List<StatisticsCountVO> displayCounts = spuMapper.getDisplayCount(search);
                Map<Long, Long> displayCountsMap = new HashMap<>();
                if (!org.springframework.util.CollectionUtils.isEmpty(displayCounts)) {
                    displayCounts.stream().forEach(item -> {
                        displayCountsMap.put(item.getId(), item.getCount());
                    });
                }

                //计算tfidf并保存
                list.stream().forEach(item -> {
                    Long displayCount = displayCountsMap.get(item.getRelateId()) != null ? displayCountsMap.get(item.getRelateId()) : 0L;
                    Long itemHaveCount = itemHaveCountMap.get(item.getRelateId()) != null ? itemHaveCountMap.get(item.getRelateId()) : 0L;
                    newSearchTfidfVO.add(SearchTfidfVO.builder().search(search).type(type).displayCount(displayCount).itemHaveCount(itemHaveCount).tagName(categoryMap.get(item.getRelateId()) != null ? categoryMap.get(item.getRelateId()).getTitle() : null).relateId(item.getRelateId()).tfidf((Double.valueOf(displayCount) / item.getTotalWordCount()) * (Math.log(Double.valueOf(item.getItemCount()) / (itemHaveCount + 1)))).searchTime(new Date()).build());
                });
            }
            operations.set("tfidf" + search + type, newSearchTfidfVO);

            Collections.sort(newSearchTfidfVO, (o1, o2) -> o2.getTfidf().compareTo(o1.getTfidf()));
        }

        //存在标签更新 不存在新建
        List<UserTag> existUserTags = userTagMapper.findAllByUserIdAndType(user.getId(), UserTagTypeEnum.SPU.getType());
        Map<Long, UserTag> existMap = new HashMap<>();
        existUserTags.stream().forEach(item -> {
            existMap.put(item.getRelateId(), item);
        });
        List<UserTag> userTags = new ArrayList<>();
        List<UserTag> updateUserTag = new ArrayList<>();
        for (int i = 0; i < newSearchTfidfVO.size() && i < 2; i++) {
            if (existMap.containsKey(newSearchTfidfVO.get(i).getRelateId())) {
                UserTag userTag = existMap.get(newSearchTfidfVO.get(i).getRelateId());
                userTag.setWeight(userTag.getWeight() + 2 - i);
                updateUserTag.add(userTag);
            } else {
                userTags.add(UserTag.builder().userId(user.getId()).type(type).weight(2 - i).relateId(newSearchTfidfVO.get(i).getRelateId()).tagName(newSearchTfidfVO.get(i).getTagName()).search(search).build());
            }

        }
        if (!org.springframework.util.CollectionUtils.isEmpty(updateUserTag)) {
            userTagMapper.updateBatchWeight(updateUserTag);
        }
        if (!org.springframework.util.CollectionUtils.isEmpty(userTags)) {
            userTagMapper.batchInsert(userTags);
        }

    }

    @Override
    public ResultData<BasePageResp<ShopSpuVO>> getShopSpuPage(SpuPageReq req) {

        if (req.getFrontCategoryId() != null) {
            List<FrontBackCategory> frontBackCategoryList = frontBackCategoryMapper.findAllByFrontId(req.getFrontCategoryId());
            if (!CollectionUtils.isEmpty(frontBackCategoryList)) {
                List<Long> ids = frontBackCategoryList.stream().map(item -> item.getBackId()).collect(Collectors.toList());
                req.setBackCategoryIdList(ids);
            }
        }

        req.setType(SpuTypeEnum.SHOP.getType());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<ShopSpuVO> list = spuMapper.findShopSpuPage(req);

        if (!list.isEmpty()) {
            Map<Long, AppointmentSetting> settingMap = daoService.getAppointmentSettingMapBySpuIds(list.stream().map(ShopSpuVO::getId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                if (settingMap.containsKey(item.getId())) {
                    item.setAppointmentSetting(settingMap.get(item.getId()));
                }
            });
        }

        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<BasePageResp<OrderCommentResp>> getCommentPage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<OrderCommentResp> list = orderCommentMapper.findAllFrontPage(req);
        if (CollectionUtils.isEmpty(list)) {
            return PageHelpUtil.buildPage(list, page);
        }
        List<Long> userIds = new ArrayList<>();
        List<Long> skuIds = new ArrayList<>();
        list.stream().forEach((item) -> {
            if (!userIds.contains(item.getUserId())) {
                userIds.add(item.getUserId());
            }
            if (!skuIds.contains(item.getSkuId())) {
                skuIds.add(item.getSkuId());
            }
        });

        // 获取用户信息
        List<User> userList = userMapper.findAllByUserIds(userIds);
        Map<Long, User> userMap = new HashMap<>();
        userList.stream().forEach(item -> userMap.put(item.getId(), item));


        // 获取商品sku信息
        Map<Long, Sku> skuMap = daoService.getSkuMap(skuIds);

        list.stream().forEach((item) -> {
            item.setAvatar(item.getAnonymous().equals((byte) 0) ? userMap.get(item.getUserId()).getAvatar() : JypConstant.ANONYMOUS_AVATAR);
            item.setNickname(item.getAnonymous().equals((byte) 0) ? userMap.get(item.getUserId()).getNickname() : SensitiveInfoUtil.chineseName(userMap.get(item.getUserId()).getNickname()));
            item.setSpecNames(skuMap.get(item.getSkuId()).getSpecNames());
            item.setSpecValues(skuMap.get(item.getSkuId()).getSpecValues());
        });

        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<CollectionShoppingCartVO> getTraceCollectionShoppingCart(User user, Long spuId) {
        if (user != null) {
            userService.createTracks(user, spuId);
            return new ResultData<>(new CollectionShoppingCartVO(userCollectionMapper.findOneByUserIdAndSpuId(user.getId(), spuId), shoppingCartMapper.countByUserId(user.getId())));
        }
        return new ResultData<>(null);
    }

    @Override
    public ResultData<String> createProductShareQrcode(User user, Long spuId, String activityType, Long activityId,
                                                       boolean share) {
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        String shareUrl = "";
        shareUrl = domainUrl + "/shopSpu?spuId=" + spuId;
        if (SpuTypeEnum.SHOP.getType().equals(spu.getType())) {
            if (ShopSpuTypeEnum.ACTIVITY.getType().equals(spu.getShopSpuType())) {
                AppointmentSetting appointmentSetting = appointmentSettingMapper.findOneBySpuId(spu.getId());
                if (!StringUtils.isEmpty(appointmentSetting.getFeature())) {
                    AppointmentFeatureDTO dto = JSON.parseObject(appointmentSetting.getFeature(), AppointmentFeatureDTO.class);
                    if (!StringUtils.isEmpty(dto.getUrl())) {
                        shareUrl = domainUrl + "/activitySpu?spuId=" + spuId;
                    }
                }
            }
        } else {
            shareUrl = domainUrl + "/share?spuId=" + spuId;
        }
        if (user != null) {
            shareUrl = shareUrl + "&inviteCode=" + inviteCodeService.generateInviteCode(user.getId());
        }

        if (activityType != null) {
            shareUrl = shareUrl + "&activityType=" + activityType + "&activityId=" + activityId;
        }
        if (share) {
            shareUrl = shareUrl + "&shareUserId=" + user.getId();
        }
        try {
            String qrcode = fileService.createQrcodeBase64(shareUrl, 100, 100);
            return new ResultData<>(qrcode);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ResultData<>(null);
    }

    @Override
    public ResultData<ZoneVO> getZoneSpuDetail(Long zoneId) {
        ZoneVO vo = new ZoneVO();
        Zone zone = zoneMapper.selectByPrimaryKey(zoneId);
        if (zone == null) {
            return new ResultData<>(null);
        }
        BeanUtils.copyProperties(zone, vo);
        List<ZoneSpuVO> zoneSpuVOList = zoneSpuMapper.findAllByZoneId(zoneId);
        if (!CollectionUtils.isEmpty(zoneSpuVOList)) {
            Map<Long, Spu> spuMap = daoService.getSpuMap(zoneSpuVOList.stream().map(ZoneSpuVO::getSpuId).collect(Collectors.toList()));
            zoneSpuVOList.stream().forEach(item -> {
                item.setName(spuMap.get(item.getSpuId()).getName());
                item.setImage(spuMap.get(item.getSpuId()).getMainImage());
                item.setPrice(spuMap.get(item.getSpuId()).getMinPrice());
                item.setFeature(spuMap.get(item.getSpuId()).getFeature());
                item.setSales(spuMap.get(item.getSpuId()).getSales());
                item.setTagList(spuMap.get(item.getSpuId()).getTagList());
                item.setType(spuMap.get(item.getSpuId()).getType());
                item.setOriginalPrice(spuMap.get(item.getSpuId()).getOriginalPrice());
            });
        }
        vo.setZoneSpuVOList(zoneSpuVOList);
        return new ResultData<>(vo);
    }


    @Override
    public ResultData<Long> editSelectionApply(SelectionApply req) {
        ResultData<Long> resultData = new ResultData<>();
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SelectionApply userSelectionApply = selectionApplyMapper.findOneByUserId(user.getId());
        if (userSelectionApply != null) {
            resultData.setResultCode(ResultCode.SELECTION_APPLY_EXIST);
            return resultData;
        }
        if (req.getId() == null) {
            req.setUserId(user.getId());
            if (req.getReturnAddress() == null) {
                req.setReturnAddress("undefine");
            }
            req.setAuditStatus(SelectionApplyStatusEnum.WAIT_AUDIT.getAuditStatus());
            req.setCreatedAt(new Date());
            req.setUpdatedAt(new Date());
            selectionApplyMapper.insert(req);
        } else {
            SelectionApply selectionApply = selectionApplyMapper.selectByPrimaryKey(req.getId());
            if (SelectionApplyStatusEnum.PASS.getAuditStatus().equals(selectionApply.getAuditStatus())) {
                resultData.setResultCode(ResultCode.NOT_EDIT_AUDIT_PASS);
                return resultData;
            }
            req.setAuditStatus(SelectionApplyStatusEnum.WAIT_AUDIT.getAuditStatus());
            req.setUpdatedAt(new Date());
            selectionApplyMapper.updateByPrimaryKeySelective(req);
        }
        resultData.setValue(req.getId());
        return resultData;
    }

    @Override
    public BaseResp deleteSelectionApply(Long id) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SelectionApply selectionApply = selectionApplyMapper.selectByPrimaryKey(id);
        if (user.getId().equals(selectionApply.getUserId())) {
            selectionApplyMapper.deleteByPrimaryKey(id);
            selectionMapper.deleteBySelectionApplyId(id);
        }
        return new BaseResp();
    }

    @Override
    public BaseResp editSelection(SelectionReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (req.getId() == null) {
            req.setUserId(user.getId());
            req.setCreatedAt(new Date());
            req.setUpdatedAt(new Date());
            req.setStatus(SelectionStatusEnum.WAIT_AUDIT.getStatus());
            selectionMapper.insert(req);
        } else {
            Selection selection = selectionMapper.selectByPrimaryKey(req.getId());
            req.setUserId(user.getId());
            req.setUpdatedAt(new Date());
            req.setSelectionApplyId(selection.getSelectionApplyId());
            req.setStatus(SelectionStatusEnum.WAIT_AUDIT.getStatus());
            selectionMapper.updateByPrimaryKeySelective(req);
        }
        if (req.getReturnAddress() != null) {
            selectionApplyMapper.updateReturnAddressById(req.getReturnAddress(), req.getId());
        }
        return new BaseResp();
    }

    @Override
    public BaseResp uploadSelection(SelectionBatchReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        List<Selection> list = req.getList();
        if (!CollectionUtils.isEmpty(list)) {
            Long selectionApplyId = list.get(0).getSelectionApplyId();
            SelectionApply selectionApply = selectionApplyMapper.selectByPrimaryKey(selectionApplyId);
            if (selectionApply != null && selectionApply.getUserId().equals(user.getId())) {
                list.stream().forEach(item -> {
                    item.setUserId(user.getId());
                    item.setCreatedAt(new Date());
                    item.setUpdatedAt(new Date());
                });
                selectionMapper.deleteBySelectionApplyId(selectionApplyId);
                selectionMapper.batchInsert(list);
            }
        }
        return new BaseResp();
    }

    @Override
    public BaseResp deleteSelection(Long id) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Selection selection = selectionMapper.selectByPrimaryKey(id);
        if (user.getId().equals(selection.getUserId())) {
            selectionMapper.deleteByPrimaryKey(id);
        }
        return new BaseResp();
    }

    @Override
    public ResultData<BasePageResp<SelectionApplyVO>> getMySelectionApplyPage(PageReq req, User user) {
        if (user == null) {
            ResultData<BasePageResp<SelectionApplyVO>> resultData = new ResultData<>();
            resultData.setResultCode(ResultCode.NOT_LOGIN);
            return resultData;
        }
        req.setUserId(user.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<SelectionApplyVO> list = selectionApplyMapper.findAllPage(req);
        if (!com.alibaba.excel.util.CollectionUtils.isEmpty(list)) {
            Map<Long, User> userMap = daoService.getUserMap(list.stream().map(SelectionApplyVO::getUserId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                item.setUserNickname(userMap.get(item.getUserId()).getUsername());
                item.setAvatar(userMap.get(item.getUserId()).getAvatar());
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }


    @Override
    public ResultData<SelectionApplyVO> getSelectionApplyDetail(User user) {
        SelectionApply selectionApply = selectionApplyMapper.findOneByUserId(user.getId());
        SelectionApplyVO vo = new SelectionApplyVO();
        if (selectionApply != null) {
            BeanUtils.copyProperties(selectionApply, vo);
            vo.setSelectionList(selectionMapper.findAllBySelectionApplyId(selectionApply.getId()));
        }
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<Selection> getSelectionDetail(Long id) {
        Selection selection = selectionMapper.selectByPrimaryKey(id);
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (user.getId().equals(selection.getUserId())) {
            return new ResultData<>(selection);
        }
        return new ResultData<>(null);
    }

    @Override
    public ResultData<BasePageResp<Selection>> getMySelectionPage(PageReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        req.setUserId(user.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Selection> list = selectionMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<List<Selection>> getMySelectionList(Long id) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SelectionApply selectionApply = selectionApplyMapper.selectByPrimaryKey(id);
        if (selectionApply.getUserId().equals(user.getId())) {
            return new ResultData<>(selectionMapper.findAllBySelectionApplyId(id));
        }
        return new ResultData<>(new ArrayList<>());
    }

    @Override
    public ResultData<BasePageResp<FlashSaleSpuItemVO>> getFlashSpuPage(PageReq req, User user) {
        List<FlashSaleActivity> activities = flashSaleActivityMapper.findAllStartActivity(new Date());
        List<Long> activityIds = new ArrayList<>();
        if (CollectionUtils.isEmpty(activities)) {
            activityIds.add(0L);
        } else {
            activityIds.addAll(activities.stream().map(FlashSaleActivity::getId).collect(Collectors.toList()));
        }
        req.setIds(activityIds);
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<FlashSaleSpuItemVO> list = flashSaleSpuMapper.findAllPageForUser2(req);
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, FlashSaleActivity> flashSaleActivityMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(activities)) {
                activities.stream().forEach(item -> {
                    flashSaleActivityMap.put(item.getId(), item);
                });
            }

            Map<Long, Spu> spuHashMap = daoService.getSpuMap(list.stream().map(FlashSaleSpuItemVO::getSpuId).collect(Collectors.toList()));
            List<FlashSaleSku> saleSkuList = flashSaleSkuMapper.findAllBySpuIdIn(list.stream().map(FlashSaleSpuItemVO::getSpuId).collect(Collectors.toList()));
            Map<Long, List<FlashSaleSku>> skuMap = new HashMap<>();
            saleSkuList.stream().forEach(item -> {
                if (item.getStock() == null) {
                    item.setStock(0);
                }
                List<FlashSaleSku> flashSaleSkuList = skuMap.get(item.getSpuId());
                if (flashSaleSkuList == null) {
                    flashSaleSkuList = new ArrayList<>();
                }
                flashSaleSkuList.add(item);
                skuMap.put(item.getSpuId(), flashSaleSkuList);
            });
            Map<Long, Sku> specSkuMap = daoService.getSkuMap(saleSkuList.stream().map(FlashSaleSku::getSkuId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                item.setMainImage(spuHashMap.get(item.getSpuId()).getMainImage());
                item.setName(spuHashMap.get(item.getSpuId()).getName());
                item.setOriginalPrice(spuHashMap.get(item.getSpuId()).getOriginalPrice());
                item.setPrice(spuHashMap.get(item.getSpuId()).getMinPrice());
                item.setFeature(spuHashMap.get(item.getSpuId()).getFeature());
                item.setFlashSaleSku(skuMap.get(item.getSpuId()).get(0));
                item.setSpecNames(specSkuMap.get(item.getFlashSaleSku().getSkuId()).getSpecNames());
                item.setSpecValues(specSkuMap.get(item.getFlashSaleSku().getSkuId()).getSpecValues());
                item.setFlashSaleActivity(flashSaleActivityMap.get(item.getActivityId()));
                item.setStock(skuMap.get(item.getSpuId()).stream().map(FlashSaleSku::getStock).reduce(0, Integer::sum));
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<BasePageResp<BossRankingVo>> getBossRanking(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        // 获取所有订单已完成的门店
        List<BossRankingVo> list = spuMapper.getBossRanking(req);
        if (CollectionUtils.isNotEmpty(list)) {
            List<BossRankingVo> newList = new ArrayList<>();
            for (BossRankingVo bossRankingVo : list) {
                // 查找当前门店信息
                ShopVO shopVO = shopMapper.findOneById(bossRankingVo.getTenantId());
                if (null == shopVO) {
                    continue;
                }
                // 查找当前门店已完成的订单信息
                List<BossRankingVo> sum = orderSkuMapper.findAllByTenantId(shopVO.getId());
                if (CollectionUtils.isEmpty(sum)) {
                    continue;
                }
                sum.forEach(s -> s.setSpu(spuMapper.findOneBySpuId(s.getSpuId())));
                shopVO.setBossRankingVos(sum);
                bossRankingVo.setShopVO(shopVO);
                newList.add(bossRankingVo);
            }
            return PageHelpUtil.buildPage(newList, page);
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<BasePageResp<SpuResp>> like(SpuPageReq req, User user) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        // 获取猜你喜欢，按销量高低排序的前30条商品
        List<SpuResp> spuResps = daoService.likeSpu(req);
        return PageHelpUtil.buildPage(spuResps, page);
    }

    @Override
    public ResultData<BasePageResp<SpuResp>> getProductDetailTwoStage(SpuPageReq req, User user) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        req.setType((byte) 3);
        List<SpuResp> spuResps = spuMapper.findAllPublicPage(req);
        List<SpuResp> list = new ArrayList<>();
        SpuResp spu = ObjectUtil.copy(spuMapper.findOneBySpuId(req.getSpuId()), SpuResp.class);
        list.add(spu);
        list.addAll(spuResps);
        Map<Long, Freight> freightMap = daoService.getFreightMap(list.stream().map(Spu::getFreightId).collect(Collectors.toList()));
        Map<Long, Brand> brandMap = daoService.getBrandMap(list.stream().map(Spu::getBrandId).collect(Collectors.toList()));
        Map<Long, Shop> shopMap = daoService.getShopMap(list.stream().map(Spu::getTenantId).collect(Collectors.toList()));
        Map<Long, Category> categoryMap = daoService.getCategoryMap(list.stream().map(Spu::getCategoryId).collect(Collectors.toList()));
        // 计算卷后价
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(l -> {
                // sku最小价格
                BigDecimal minPrice = spuMapper.getMinPrice(l.getId());
                // 是否参与优惠券
                Coupon coupon = couponMapper.getBySpuId(l.getId());
                if (coupon != null && minPrice.compareTo(coupon.getDiscountAmount()) >= 0) {
                    l.setDiscountAmount(l.getPartnerPrice().subtract(coupon.getDiscountAmount()));
                }
                l.setFreightName(freightMap.get(l.getFreightId()).getName());
                l.setCategoryTitle(categoryMap.get(l.getCategoryId()).getTitle());
                l.setBrandName(brandMap.containsKey(l.getBrandId()) ? brandMap.get(l.getBrandId()).getName() : null);
                l.setShopAvatar(shopMap.get(l.getTenantId()).getAvatar());
                l.setShopNickname(shopMap.get(l.getTenantId()).getShopName());

                CommentStatistics commentStatistics = commentStatisticsMapper.findOneBySpuId(l.getId());
                if (commentStatistics == null) {
                    commentStatistics = new CommentStatistics();
                }
                l.setCommentStatistics(commentStatistics);
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }
}